/**
 * Copyright 2010 Creative Works, Inc.
 * Creative Works licenses this file to you under the Apache License, version
 * 2.0 (the "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *    http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied.  See the License for the specific language governing
 * permissions and limitations under the License.
 */
package org.sendvox.freswitch.core.impl;

import java.util.logging.Level;
import java.util.logging.Logger;

import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.handler.codec.frame.TooLongFrameException;
import org.sendvox.freswitch.event.impl.RawMessage;
import org.sendvox.freswitch.inbound.impl.ResponseCallbacks;

public class EventSocketLibDigest {

	private final Logger logger = Logger.getLogger(EventSocketLibDigest.class
			.getName());

	/**
	 * carriage-return character
	 */
	private static final byte EOL = 10;

	/**
	 * Reads the events from FreeSWITCH's <code>mod_event_socket</code>; the
	 * data will be in the format of name/value pairs, separated by a colon. An
	 * event message is terminated with two end-of-line (EOL) sequences.
	 * 
	 * @param currentMessage
	 *            the {@code EventSocketLibResponse} object with the header and
	 *            body of the event message
	 * @param buffer
	 *            an abstract view for one or more primitive byte arrays
	 *            (byte[]) and NIO buffers
	 * @param maxHeaderSize
	 *            the max size of each header line
	 * @param treatUnknownHeadersAsBody
	 *            if {@code true} treats headers not listed on
	 *            {@code EventSocketLibHeader} enumeration as a event message
	 *            body <code>default:false</code>
	 * @throws TooLongFrameException
	 */
	public void extractSocketResponseHeader(
			RawMessage currentMessage, ChannelBuffer buffer,
			int maxHeaderSize, boolean treatUnknownHeadersAsBody)
			throws TooLongFrameException {

		StringBuilder headerLine = new StringBuilder(64);
		byte b = 0;
		int counter = 0;
		while (buffer.readable() && (b = buffer.readByte()) != 0) {
			if ((char) b == EOL) {
				if (!headerLine.toString().isEmpty()) {
					String headerName = headerLine.toString().split(":")[0]
							.trim();
					ResponseCallbacks header = ResponseCallbacks
							.fromLiteral(headerName);
					if (header == null) {
						if (treatUnknownHeadersAsBody) {
							currentMessage.addBodyLine(headerLine.toString());
						} else {
							throw new IllegalStateException(
									"Unhandled ESL header ["
											+ headerLine.toString().split(":")[0]
											+ ']');
						}
					}
					String headerValue = headerLine.toString().split(":")[1]
							.trim();
					currentMessage.addHeader(header, headerValue);
					logger.log(Level.FINE, headerLine.toString());
					counter = 0;
				}
				headerLine = new StringBuilder(64);
			} else {
				// Abort decoding if the decoded line is too large.
				if (headerLine.length() >= maxHeaderSize) {
					throw new TooLongFrameException(
							"ESL message line is longer than " + maxHeaderSize
									+ " bytes.");
				}
				headerLine.append((char) b);
			}

			if (headerLine.toString().isEmpty() && counter++ == 1)
				break;
		}
	}

	/**
	 * Frames the events from FreeSWITCH's <code>mod_event_socket</code> by
	 * reading each body line until LFs are encountered. Any multiline header
	 * data is URL encoded so it still appears as 1 line on the response.
	 * 
	 * @param currentMessage
	 *            the {@code EventSocketLibResponse} object with the header and
	 *            body of the event message
	 * @param buffer
	 *            an abstract view for one or more primitive byte arrays
	 *            (byte[]) and NIO buffers
	 * @param contentLength
	 */
	public void extractSocketResponseBody(RawMessage currentMessage,
			ChannelBuffer buffer, int contentLength) {

		StringBuilder bodyLine = new StringBuilder(64);
		byte b = 0;
		while (buffer.readable() && (b = buffer.readByte()) != 0) {
			if ((char) b == EOL) {
				if (!bodyLine.toString().isEmpty()) {
					currentMessage.addBodyLine(bodyLine.toString());
					logger.log(Level.FINE, bodyLine.toString());
					bodyLine = new StringBuilder(64);
				}
			} else {
				bodyLine.append((char) b);
				//System.out.println(bodyLine);
			}
		}

	}
}
